﻿/****************************************************************************************
 * Copyright © 2020 - 2023 by AngryKeyboard.
 * SFML_俄罗斯方块 is licensed under MulanOWL BY v1.
 * You can use this software according to the terms and conditions of the MulanOWL BY v1.
 * You may obtain a copy of MulanOWL BY v1 at:
 *          http://license.coscl.org.cn/MulanOWLBYv1
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the MulanOWL BY v1 for more details.
****************************************************************************************/

/*
调试须知：
  游戏相关文件若有加载失败的情况，根据调试窗口返回值对应查找问题所在
  -1：方块动态存储分配失败
  -2：窗口创建失败
  11：图片加载失败
  12：背景乐加载失败
  13：音效加载失败
  14：字体文件加载失败
  21：autoPressKey获取动态存储异常
**********************************************************/

#include <fstream>
#include <string>
#include <sstream>
#include "CGameWindow.h"
#include <tchar.h>

using std::ifstream;
using std::string;
using std::stringstream;

CGameWindow::CGameWindow()
  :m_pBestBlock(nullptr),
  m_nBlockCounter(0), m_nScore(0), m_fDelay(SPEED_EASY),
  m_bStart(false), m_bPause(false), m_bGameOver(false), m_bAutoFill(false),
  m_nHardLevel(HARD_LEVEL_NORMAL) {
  //加载图片文件
  bool flags[3] = { false };//检查图片文件读取情况
  flags[0] = m_TextureBlock.loadFromFile("Resource/images/tiles.png");
  flags[1] = m_TextureBg.loadFromFile("Resource/images/bg.jpg");
  flags[2] = m_TextureFrame.loadFromFile("Resource/images/frame.png");
  for (int i = 0; i < 3; i++) {
    //如果文件读取失败
    if (flags[i] == false) {
      exit(11);//直接退出，返回图片加载失败代码
    }
  }
  //加载绘图对象
  m_SpriteBlock.setTexture(m_TextureBlock);
  m_SpriteBg.setTexture(m_TextureBg);
  m_SpriteFrame.setTexture(m_TextureFrame);

  //加载音乐文件
  if (!m_MusicBg.openFromFile("Resource/sounds/bg.wav")) {
    exit(12);//直接退出，返回音乐加载失败代码
  }
  if (!m_Buffer_ClearLine.loadFromFile("Resource/sounds/xiaochu.wav")) {
    exit(13);//直接退出，返回音效加载失败代码
  }
  m_Sound_ClearLine.setBuffer(m_Buffer_ClearLine);// 设置声音缓存
  memset(m_arrnTable, 0, sizeof(m_arrnTable));//清空数组区域

  //创建窗口
  m_pWindow = new RenderWindow(
    VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), //窗口模式
    _T("智能俄罗斯方块"));                //标题
  if (m_pWindow == nullptr) {
    exit(-2);
  }
  m_pWindow->setFramerateLimit(30);//设置帧率上限
  renewIcon();//设置图标

  srand(time(0)); //设置随机数种子
  initText();   //初始化文本

  keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);//复位↓按键，避免方块一直加速
}

CGameWindow::~CGameWindow() {
  if (m_pBestBlock != nullptr) {
    delete m_pBestBlock;
    m_pBestBlock = nullptr;
  }
  if (m_pWindow != nullptr) {
    delete m_pWindow;
    m_pWindow = nullptr;
  }
}

void CGameWindow::start() {
  //游戏开始界面
  while (m_pWindow->isOpen()) {
    keyEvent_start();

    renewText();
    display();

    //如果游戏已经开始
    if (m_bStart) {
      break;  //就退出循环
    }

    sf::sleep(Time(sf::milliseconds(5)));//延时，减少CPU负担
  }

  run();  //游戏运行
}

void CGameWindow::checkGameOver(vector<CBlock>::iterator block) {
  const SPoint *pos = block->Pos();
  //检查方块所有坐标中是否有超过上边界的
  for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
    //如果某一点的行坐标越界
    if (pos[i].row < 0) {
      m_bGameOver = true;//设置游戏结束标记
    }
  }
}

void CGameWindow::clearLines() {
  int nClearLines = 0;

  for (int i = 0; i < ROW_COUNT; i++) {
    int count = 0;
    for (int j = 0; j < COL_COUNT; j++) {
      //如果这个格子不为空
      if (m_arrnTable[i][j]) {
        count++;//计数
      }
    }

    if (count < COL_COUNT) {
      //没有满行就继续扫描下一行
      continue;
    }

    //满行时进行消行
    if (i == 0) {
      //如果满行是最顶部的那行，就清零。
      memset(m_arrnTable, 0, COL_COUNT * sizeof(int));
    }
    else {
      //否则把这一行上面的内容全部复制
      memmove(m_arrnTable + 1, m_arrnTable, i * COL_COUNT * sizeof(int));
    }

    nClearLines += 1;//记录消除的行数
    m_Sound_ClearLine.play();
  }

  renewScore(nClearLines);//根据行数计分
  renewText();//刷新界面文本
}

void CGameWindow::display() {
  m_pWindow->clear();//清屏
  m_pWindow->draw(m_SpriteBg);//画背景
  drawBlocks();//画方块
  m_pWindow->draw(m_SpriteFrame);//画边框
  m_pWindow->draw(m_TextScore); //分数
  m_pWindow->draw(m_TextLines);//消除行数
  m_pWindow->draw(m_Text_BlockCounter);//方块计数
  m_pWindow->draw(m_TextHardLevel);//难度等级
  m_pWindow->draw(m_TextAuto);//自动模式
  m_pWindow->draw(m_TextBornTimer);//时间

  //显示游戏信息
  if (!m_bStart) {
    m_pWindow->draw(m_TextGameInfo);
  }

  //显示暂停
  if (m_bPause) {
    m_pWindow->draw(m_TextPause);
  }

  //显示结束
  if (m_bGameOver) {
    m_pWindow->draw(m_TextGameOver);
  }

  m_pWindow->display();//显示
}

void CGameWindow::drawBlocks() {
  // 绘制已降落完毕的方块
  for (int i = 0; i < ROW_COUNT; i++) {
    for (int j = 0; j < COL_COUNT; j++) {
      if (m_arrnTable[i][j] == 0) continue;
      m_SpriteBlock.setTextureRect(IntRect((m_arrnTable[i][j] - 1) * POINT_WIDTH, 0, POINT_WIDTH, POINT_WIDTH));//截取图片中想要绘制的部分
      m_SpriteBlock.setPosition(j * POINT_WIDTH, i * POINT_WIDTH);//设置绘图坐标
      m_SpriteBlock.move(LEFT_MARGIN, TOP_MARGIN); //移动整个要绘制的图片到指定区域
      m_pWindow->draw(m_SpriteBlock);
    }
  }

  //如果容器为空，跳过后面的操作
  if (m_vBlocks.empty()) {
    return;
  }
  //绘制容器中的方块
  for (auto it = m_vBlocks.begin(); it != m_vBlocks.end(); it++) {
    for (int i = 0; i < 4; i++) {
      int blockIndex = it->Shape();   //获取形状编号
      const SPoint *curBlock = it->Pos();//获取方块坐标
      m_SpriteBlock.setTextureRect(IntRect(blockIndex * POINT_WIDTH, 0, POINT_WIDTH, POINT_WIDTH));//截取图片中想要绘制的部分
      m_SpriteBlock.setPosition(curBlock[i].column * POINT_WIDTH, curBlock[i].row * POINT_WIDTH);//设置绘图坐标
      m_SpriteBlock.move(LEFT_MARGIN, TOP_MARGIN); //移动整个要绘制的图片到指定区域
      m_pWindow->draw(m_SpriteBlock);
    }
  }
}

void CGameWindow::getBestBlock() {
  if (m_vBlocks.empty()) {
    return;
  }

  //如果还未得到最佳方块，就运算获取
  if (m_pBestBlock != nullptr) {
    delete m_pBestBlock;
  }
  m_pBestBlock = CAutoFill::getBestBlock(*m_vBlocks.begin(), m_arrnTable);
  //如果autoFill获取动态存储异常
  if (!m_pBestBlock) {
    exit(21);
  }
}

void CGameWindow::keyEvent_run() {
  Event e;

  while (m_pWindow->pollEvent(e)) {
    //关闭窗口检查
    if (e.type == Event::Closed)
      m_pWindow->close();

    if (e.type == Event::KeyPressed) {
      //先检查“暂停”和“重新开始”
      switch (e.key.code) {
        case Keyboard::P:
          if (!m_bGameOver) {
            m_bPause = !m_bPause;//翻转暂停标记
          }
          break;
        case Keyboard::R:
          //keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);//复位↓按键，避免方块一直加速
          rePlay();//重新开始
          break;
        default:
          break;
      }

      //检查难度切换按钮
      switch (e.key.code) {
        case Keyboard::A:
          m_bAutoFill = !m_bAutoFill;//自动填充翻转
          //keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);//复位↓按键，避免方块一直加速
          break;
        case Keyboard::Num1:
        case Keyboard::Numpad1:
          m_nHardLevel = HARD_LEVEL_EASY;
          break;
        case Keyboard::Num2:
        case Keyboard::Numpad2:
          m_nHardLevel = HARD_LEVEL_NORMAL;
          break;
        case Keyboard::Num3:
        case Keyboard::Numpad3:
          m_nHardLevel = HARD_LEVEL_CHALLENGE;
          break;
        default:
          break;
      }

      //游戏结束或暂停：跳过其它按键
      if (m_bGameOver || m_bPause) {
        return;//直接退出函数，后续语句不执行
      }

      //界面上没有任何方块时，跳过其它按键
      if (m_vBlocks.empty()) {
        return;
      }

      //正常游戏时的按键检查
      switch (e.key.code) {
        case Keyboard::Up:
          m_vBlocks.begin()->rotate(m_arrnTable);
          break;
        case Keyboard::Left:
          m_vBlocks.begin()->moveLeftRight(m_arrnTable, -1);
          break;
        case Keyboard::Right:
          m_vBlocks.begin()->moveLeftRight(m_arrnTable, 1);
          break;
        case Keyboard::Space:
          m_vBlocks.begin()->dropToBottom(m_arrnTable);
          checkGameOver(m_vBlocks.begin());//检查是否超过上边界
          renewTable(m_vBlocks.begin());//固化到桌面
          m_vBlocks.erase(m_vBlocks.begin());
          break;
        default:
          break;
      }
    }
  }
  // 长按按键事件处理，下降
  if (m_pWindow->hasFocus()) {
    if (!m_bGameOver && !m_bPause) {
      if (Keyboard::isKeyPressed(Keyboard::Down)) {
        m_fDelay = SPEED_QUICK;
      }
    }
  }
}

void CGameWindow::keyEvent_start() {
  Event e;

  while (m_pWindow->pollEvent(e)) {
    //关闭窗口检查
    if (e.type == Event::Closed) {
      m_pWindow->close();
    }

    if (e.type == Event::KeyPressed) {
      //检查按钮
      switch (e.key.code) {
        //自动填充切换
        case Keyboard::A:
          m_bAutoFill = !m_bAutoFill;//自动填充翻转
          break;

          //游戏开始
        case Keyboard::R:
          m_bStart = true;
          break;

          //难度切换
        case Keyboard::Num1:
        case Keyboard::Numpad1:
          m_nHardLevel = HARD_LEVEL_EASY;
          break;
        case Keyboard::Num2:
        case Keyboard::Numpad2:
          m_nHardLevel = HARD_LEVEL_NORMAL;
          break;
        case Keyboard::Num3:
        case Keyboard::Numpad3:
          m_nHardLevel = HARD_LEVEL_CHALLENGE;
          break;
        default:
          break;
      }
    }
  }
}

void CGameWindow::initText() {
  //开始初始化字体

  //打开字体文件
  if (!m_Font.loadFromFile("Resource/STXINGKA.TTF")) {
    exit(14);
  }

  //显示时间
  m_TextBornTimer.setFont(m_Font); // 字体
  m_TextBornTimer.setCharacterSize(18);// 字号
  m_TextBornTimer.setFillColor(sf::Color::White); // 颜色
  //m_TextBornTimer.setStyle(sf::Text::Bold); // 粗体
  m_TextBornTimer.setPosition(235, 40);//显示位置
  m_TextBornTimer.setString(_T("时间：\n0"));//内容
  m_TextBornTimer.setOutlineThickness(4);//描边

  //设置自动填充
  m_TextAuto.setFont(m_Font); // 字体
  m_TextAuto.setCharacterSize(18);// 字号
  m_TextAuto.setFillColor(sf::Color::White); // 颜色
  //m_TextAuto.setStyle(sf::Text::Bold); // 粗体
  m_TextAuto.setPosition(235, 105);//显示位置
  m_TextAuto.setString(_T("自动填充：\n关"));//内容
  m_TextAuto.setOutlineThickness(4);//描边

  //设置难度等级
  m_TextHardLevel.setFont(m_Font); // 字体
  m_TextHardLevel.setCharacterSize(18);// 字号
  m_TextHardLevel.setFillColor(sf::Color::White); // 颜色
  //m_TextHardLevel.setStyle(sf::Text::Bold); // 粗体
  m_TextHardLevel.setPosition(235, 170);//显示位置
  m_TextHardLevel.setString(_T("难度：\n普通"));//内容
  m_TextHardLevel.setOutlineThickness(4);//描边

  //设置分数
  m_TextScore.setFont(m_Font); // 字体
  m_TextScore.setCharacterSize(18);// 字号
  m_TextScore.setFillColor(sf::Color::White); // 颜色
  //m_TextScore.setStyle(sf::Text::Bold); // 粗体
  m_TextScore.setPosition(235, 235);//显示位置
  m_TextScore.setString(_T("得分：\n0"));//内容
  m_TextScore.setOutlineThickness(4);//描边

  //设置已消除行数
  m_TextLines.setFont(m_Font); // 字体
  m_TextLines.setCharacterSize(18);// 字号
  m_TextLines.setFillColor(sf::Color::White); // 颜色
  //m_TextLines.setStyle(sf::Text::Bold); // 粗体
  m_TextLines.setPosition(235, 300);//显示位置
  m_TextLines.setString(_T("消除行数：\n0"));//内容
  m_TextLines.setOutlineThickness(4);//描边

  //设置方块计数
  m_Text_BlockCounter.setFont(m_Font);
  m_Text_BlockCounter.setCharacterSize(18);
  m_Text_BlockCounter.setFillColor(sf::Color::White);
  //m_Text_BlockCounter.setStyle(sf::Text::Bold);
  m_Text_BlockCounter.setPosition(235, 365);
  m_Text_BlockCounter.setString(_T("方块数：\n0"));
  m_Text_BlockCounter.setOutlineThickness(4);

  //游戏信息
  m_TextGameInfo.setFont(m_Font); // 字体
  m_TextGameInfo.setCharacterSize(18);// 字号
  m_TextGameInfo.setFillColor(sf::Color::White); // 颜色
  //m_TextGameInfo.setStyle(sf::Text::Bold); // 粗体
  m_TextGameInfo.setPosition(35, 95);//显示位置
  m_TextGameInfo.setString(
    _T(
      "\t\t游戏信息\n"
      "1：切换简单难度\n"
      "2：切换普通难度\n"
      "3：切换挑战难度\n"
      "A：自动填充\n"
      "P：暂停\n"
      "R：开始、重来\n"
      "↑：旋转\n"
      "←↓→：移动\n"
      "空格：落到底部\n"
    )
  );//内容
  m_TextGameInfo.setOutlineThickness(4);//描边

  //暂停信息
  m_TextPause.setFont(m_Font); // 字体
  m_TextPause.setOutlineThickness(4);
  m_TextPause.setCharacterSize(18);// 字号
  m_TextPause.setFillColor(sf::Color::White); // 颜色
  //m_TextPause.setStyle(sf::Text::Bold);
  m_TextPause.setString(_T("暂    停"));
  m_TextPause.setPosition(100, 170);

  //游戏结束信息
  m_TextGameOver.setFont(m_Font); // 字体
  m_TextGameOver.setOutlineThickness(4);
  m_TextGameOver.setCharacterSize(18);// 字号
  m_TextGameOver.setFillColor(sf::Color::White); // 颜色
  //m_TextGameOver.setStyle(sf::Text::Bold);
  m_TextGameOver.setString(_T("游戏结束"));
  m_TextGameOver.setPosition(70, 170);
}

void CGameWindow::newBlcok() {
  float delay;
  //检查是否该生成方块（不同难度间隔不同）
  switch (m_nHardLevel) {
    case HARD_LEVEL_EASY:
      delay = BORN_CD_EASY;
      break;
    case HARD_LEVEL_NORMAL:
      delay = BORN_CD_NORMAL;
      break;
    case HARD_LEVEL_CHALLENGE:
      delay = BORN_CD_CHALLENGE;
      break;
    default:
      delay = 0;
      break;
  }
  //如果不满足新方块生成的条件，退出
  if ((m_Clock_BornTimer.getElapsedTime().asSeconds() < delay) &&
    (!m_vBlocks.empty())) {
    return;
  }

  m_vBlocks.push_back(CBlock());//添加一个方块到容器
  m_nBlockCounter++;//方块计数+1
  renewText();
  renewIcon();

  m_Clock_BornTimer.restart();
}

void CGameWindow::renewBlocks() {
  static Clock fall_clock;//判断是否到了下落一格的时间
  float delay;

  newBlcok();

  //如果当前容器为空，跳过后续操作
  if (m_vBlocks.empty()) {
    return;
  }

  //根据难度设置延时
  switch (m_nHardLevel) {
    case HARD_LEVEL_EASY:
      delay = SPEED_EASY;
      break;
    case HARD_LEVEL_NORMAL:
      delay = SPEED_NORMAL;
      break;
    case HARD_LEVEL_CHALLENGE:
      delay = SPEED_CHALLENGE;
      break;
    default:
      delay = 0;
      break;
  }
  //如果该下落一格了
  if (fall_clock.getElapsedTime().asSeconds() >= delay) {
    //让所有方块下落一格（被控制的方块除外）
    for (auto it = m_vBlocks.begin() + 1; it < m_vBlocks.end(); ) {
      //如果下落时需要固定
      if (it->drop1Step(m_arrnTable)) {
        renewTable(it);//固化
        it = m_vBlocks.erase(it);//从容器中删除
      }
      else {
        it++;
      }
    }

    fall_clock.restart();
  }
}

void CGameWindow::renewCurBlock() {
  //如果当前容器为空直接返回
  if (m_vBlocks.empty()) {
    return;
  }

  static Clock curFallClock;// 计时器

  //如果到了降落的时间
  if (curFallClock.getElapsedTime().asSeconds() >= m_fDelay) {
    bool flag = false;//接收drop1Step返回值，判断方块是否落到底部
    flag = m_vBlocks.begin()->drop1Step(m_arrnTable); //下降一个位置

    //如果下降到底部了
    if (flag) {
      checkGameOver(m_vBlocks.begin());//先检查是否超过上边界
      renewTable(m_vBlocks.begin());//固化到桌面
      m_vBlocks.erase(m_vBlocks.begin());//删除这个落地的方块
    }
    curFallClock.restart(); //计时器复位
  }

  //重置当前方块下落速度
  switch (m_nHardLevel) {
    case HARD_LEVEL_EASY:
      m_fDelay = SPEED_EASY;
      break;
    case HARD_LEVEL_NORMAL:
      m_fDelay = SPEED_NORMAL;
      break;
    case HARD_LEVEL_CHALLENGE:
      m_fDelay = SPEED_CHALLENGE;
      break;
    default:
      break;
  }
}

void CGameWindow::renewIcon() {
  Image temp = m_TextureBlock.copyToImage();//获取图像

  int blockIndex;   //获取形状编号
  if (m_vBlocks.empty()) {
    //没有方块时使用随机图标
    blockIndex = rand() % BLOCK_TYPE_COUNT;
  }
  else {
    //方块存在，就显示当前方块为图标
    blockIndex = m_vBlocks.begin()->Shape();
  }

  m_SpriteBlock.setTextureRect(IntRect(blockIndex * POINT_WIDTH, 0, POINT_WIDTH, POINT_WIDTH));//截取图片中想要绘制的部分

  IntRect ret = m_SpriteBlock.getTextureRect();//获取矩形信息
  Texture iconTexture;
  iconTexture.loadFromImage(temp, ret);//加载图形中的指定区域
  m_pWindow->setIcon(POINT_WIDTH, POINT_WIDTH, iconTexture.copyToImage().getPixelsPtr());//设置图标
}

void CGameWindow::renewTable(vector<CBlock>::iterator block) {
  //如果游戏已经结束，直接退出
  if (m_bGameOver) {
    return;
  }

  int blockIndex = block->Shape();//获取方块编号
  const SPoint *BlockPos = block->Pos();//获取坐标点
  //固化方块到桌面
  for (int i = 0; i < 4; i++) {
    int row = BlockPos[i].row;
    int col = BlockPos[i].column;
    //检查行列值，避免数组访问越界
    if (row >= 0 && row < ROW_COUNT && col >= 0 && col < COL_COUNT) {
      // 设置标记，“固化”对应位置
      m_arrnTable[row][col] = blockIndex + 1;//标记设置为非0值
    }
  }
}

void CGameWindow::renewText() {
  //刷新分数
  TCHAR tmp1[32] = _T("得分：\n");//标题
  TCHAR tmp2[32];//数值
  _stprintf_s(tmp2, _T("%d"), m_nScore);//数字转字符串
  //strcat_s(tmp1, sizeof(tmp1), tmp2);//连接字符串
  _tcsncat_s(tmp1, tmp2, sizeof(tmp1));//连接字符串
  m_TextScore.setString(tmp1);

  //刷新方块计数
  //strcpy_s(tmp1, sizeof(tmp1), "Blocks:\n");
  //sprintf_s(tmp2, "%d", m_nBlockCounter);//数字转字符串
  //strcat_s(tmp1, sizeof(tmp1), tmp2);//连接字符串
  _tcscpy_s(tmp1, _T("方块数：\n"));
  _stprintf_s(tmp2, _T("%d"), m_nBlockCounter);
  _tcsncat_s(tmp1, tmp2, sizeof(tmp1));//连接字符串
  m_Text_BlockCounter.setString(tmp1);

  //刷新方块计数
  //strcpy_s(tmp1, sizeof(tmp1), "Lines:\n");
  //sprintf_s(tmp2, "%d", m_nLines);//数字转字符串
  //strcat_s(tmp1, sizeof(tmp1), tmp2);//连接字符串
  _tcscpy_s(tmp1, _T("消除行数：\n"));
  _stprintf_s(tmp2, _T("%d"), m_nLines);
  _tcsncat_s(tmp1, tmp2, sizeof(tmp1));//连接字符串
  m_TextLines.setString(tmp1);

  //刷新时间
  //strcpy_s(tmp1, sizeof(tmp1), "Time:\n");
  //sprintf_s(tmp2, "%.3f", m_Clock_BornTimer.getElapsedTime().asSeconds());//数字转字符串
  //strcat_s(tmp1, sizeof(tmp1), tmp2);//连接字符串
  _tcscpy_s(tmp1, _T("时间：\n"));
  _stprintf_s(tmp2, _T("%.3f"), m_Clock_BornTimer.getElapsedTime().asSeconds());
  _tcsncat_s(tmp1, tmp2, sizeof(tmp1));//连接字符串
  m_TextBornTimer.setString(tmp1);

  //刷新难度等级文本
  //strcpy_s(tmp1, sizeof(tmp1), "Level:\n");
  _tcscpy_s(tmp1, _T("难度：\n"));
  switch (m_nHardLevel)//根据等级显示字符串
  {
    case HARD_LEVEL_EASY:
      //strcpy_s(tmp2, sizeof(tmp2), "Easy");
      _tcscpy_s(tmp2, _T("简单"));
      break;
    case HARD_LEVEL_NORMAL:
      //strcpy_s(tmp2, sizeof(tmp2), "Normal");
      _tcscpy_s(tmp2, _T("普通"));
      break;
    case HARD_LEVEL_CHALLENGE:
      //strcpy_s(tmp2, sizeof(tmp2), "Challenge");
      _tcscpy_s(tmp2, _T("挑战"));
      break;
    default:
      break;
  }
  //strcat_s(tmp1, sizeof(tmp1), tmp2);//连接字符串
  _tcsncat_s(tmp1, tmp2, sizeof(tmp1));//连接字符串
  m_TextHardLevel.setString(tmp1);

  //刷新自动模式文本
  //strcpy_s(tmp1, sizeof(tmp1), "Auto:\n");
  _tcscpy_s(tmp1, _T("自动填充：\n"));
  switch (m_bAutoFill)//根据等级显示字符串
  {
    case false:
      //strcpy_s(tmp2, sizeof(tmp2), "OFF");
      _tcscpy_s(tmp2, _T("关"));
      break;
    case true:
      //strcpy_s(tmp2, sizeof(tmp2), "ON");
      _tcscpy_s(tmp2, _T("开"));
      break;
  }
  //strcat_s(tmp1, sizeof(tmp1), tmp2);//连接字符串
  _tcsncat_s(tmp1, tmp2, sizeof(tmp1));//连接字符串
  m_TextAuto.setString(tmp1);
}

void CGameWindow::renewScore(int lines) {
  m_nLines += lines;//刷新已消除行数

  //根据消除行数加分
  switch (lines) {
    case 1:
      m_nScore += 100;
      break;
    case 2:
      m_nScore += 300;
      break;
    case 3:
      m_nScore += 700;
      break;
    case 4:
      m_nScore += 1500;
      break;
    default:
      break;
  }
}

void CGameWindow::rePlay() {
  m_nScore = 0;//分数清空
  m_nBlockCounter = 0;//方块计数清空
  m_nLines = 0;

  m_bStart = false;
  m_bPause = false;//重置暂停状态
  m_bGameOver = false;
  m_bAutoFill = false;

  memset(m_arrnTable, 0, sizeof(m_arrnTable));//重置桌面
  //重置方块
  m_vBlocks.clear();//清空容器
  if (m_pBestBlock)//重置AI方块
  {
    delete m_pBestBlock;
    m_pBestBlock = nullptr;
  }

  m_MusicBg.stop();//重置音乐播放位置
  start();  //回到开始界面
}

void CGameWindow::run() {
  //循环播放音乐
  m_MusicBg.setLoop(true);

  //newBlock();   //生成第一个方块
  newBlcok(); //生成第一个方块（测试集版本）

  //进入游戏循环
  while (m_pWindow->isOpen()) {
    //如果当前窗口没被选中，设置为暂停状态
    if (!m_pWindow->hasFocus()) {
      //m_bPause = true;
    }

    //处理窗口事件，不处理会导致窗口卡住（SFML特性？）
    keyEvent_run();

    //非 暂停、结束状态时才能下降方块
    if (m_bPause == false && m_bGameOver == false) {
      renewCurBlock();//刷新当前方块状态
      renewBlocks();//刷新方块容器
      autoPressKey();//自动控制检查
      clearLines();//清除满行

      //如果背景乐被暂停了，就继续播放
      if (m_MusicBg.getStatus() == Sound::Paused || m_MusicBg.getStatus() == Sound::Stopped) {
        m_MusicBg.play();
      }
    }
    else {
      //音乐暂停
      m_MusicBg.pause();
    }

    display();//刷新画面
    sf::sleep(Time(sf::milliseconds(5)));//延时，减少CPU负担
  }
}

void CGameWindow::autoPressKey() {
  static Clock autoControlClock;

  //如果界面上没有方块
  if (m_vBlocks.empty()) {
    return;
  }

  //操控间隔
  if (!m_bAutoFill || autoControlClock.getElapsedTime().asSeconds() < 0.06) {
    return;//没到控制时间就退出
  }

  getBestBlock();
  //keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);//复位↓按键，避免方块一直加速


  autoControlClock.restart(); //计时器重启计时

  //根据方块信息转向
  //当前方块与自动运算获取的方块方向不同时，转向
  if (m_vBlocks.begin()->Direction() != m_pBestBlock->Direction()) {
    //发送键盘消息
    //参数1：键值
    //参数3：消息类型，0为按下，另可用宏KEYEVENTF_KEYUP\KEYEVENTF_EXTENDEDKEY\KEYEVENTF_SCANCODE\KEYEVENTF_UNICODE等？
    //参考资料：https://blog.csdn.net/weixin_34146805/article/details/93423763
    //keybd_event(VK_UP, 0, 0, 0);
    //keybd_event(VK_UP, 0, KEYEVENTF_KEYUP, 0);
    m_vBlocks[0].rotate(m_arrnTable);
    return;//直接退出（一次只发一个消息）
  }

  //根据方块坐标左右移动
  //计算两个方块的相对位置
  int offset = m_pBestBlock->Pos()->column - m_vBlocks.begin()->Pos()->column;
  //如果BestBlock在当前方块的左边
  if (offset < 0) {
    //keybd_event(VK_LEFT, 0, 0, 0);
    //keybd_event(VK_LEFT, 0, KEYEVENTF_KEYUP, 0);
    m_vBlocks[0].moveLeftRight(m_arrnTable, -1);
    return;
  }
  //如果BestBlock在当前方块的右边
  if (offset > 0) {
    //keybd_event(VK_RIGHT, 0, 0, 0);
    //keybd_event(VK_RIGHT, 0, KEYEVENTF_KEYUP, 0);
    m_vBlocks[0].moveLeftRight(m_arrnTable, 1);
    return;
  }

  //控制下落方式
  //keybd_event(VK_DOWN, 0, 0, 0);//平滑下落
  m_vBlocks[0].drop1Step(m_arrnTable);


  //直接坠落
  //keybd_event(VK_SPACE, 0, 0, 0);//按下
  //keybd_event(VK_SPACE, 0, KEYEVENTF_KEYUP, 0);//放开
}
